package org.apache.sad.core.framework.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.InflaterInputStream;

public abstract class ObjectUtils {

    //TODO buffer size - typical size of ENI batch
    public static byte[] serializeObject(Object object) throws IOException {
        if (object == null) {
            return null;
        }
        
        ByteArrayOutputStream baos = null;
        ObjectOutputStream oos = null;
        try {
            baos = new ByteArrayOutputStream();  
            oos = new ObjectOutputStream(baos);  
            oos.writeObject(object);  
            oos.flush(); 
            return baos.toByteArray();  
        } finally {
            close(oos);
            close(baos);
        }
    }
       
    public static Object deserializeObject(byte[] rawData) 
        throws IOException, ClassNotFoundException {
        if (rawData == null) {
            return null;
        }
        
        ObjectInputStream ois = null;
        ByteArrayInputStream data = null;
        try {
            data = new ByteArrayInputStream(rawData); 
            ois = new ObjectInputStream(data);  
            return ois.readObject(); 
        } finally {
            close(ois);
            close(data);
        }
    }
    
    public static byte[] compressData(byte[] data) throws IOException {
        return compressData(data, Deflater.BEST_COMPRESSION);
    }
    
    public static byte[] compressData(byte[] data, int deflaterLevel) throws IOException {
        if (data == null) {
            return null;
        }
        
        ByteArrayOutputStream baos = null;
        DeflaterOutputStream dout = null;
        try {
            baos = new ByteArrayOutputStream(data.length);            
            Deflater deflater = new Deflater(deflaterLevel);
            dout = new DeflaterOutputStream(baos, deflater); 
            dout.write(data);  
            dout.finish();            
            return baos.toByteArray();  
        } finally {
            close(dout);
            close(baos);
        } 
    }   
    
    public static byte[] decompressData(byte[] data) throws IOException {
        if (data == null) {
            return null;
        }
        
        ByteArrayOutputStream baos = null;
        ByteArrayInputStream bais = null;
        InflaterInputStream iin = null;
        try {
            //result container
            baos = new ByteArrayOutputStream(data.length * 2); 
            bais = new ByteArrayInputStream(data);
            iin = new InflaterInputStream(bais);
            //read data out using buffer
            byte[] buf = new byte[1024];  
            int len = 0; 
            while ((len = iin.read(buf)) > 0) {  
                baos.write(buf, 0, len);  
            }   
            return baos.toByteArray();  
        } finally {
            close(iin);
            close(bais);
            close(baos);
        } 
    } 
    
    public static Object inflateObject(byte[] rawData) 
        throws IOException, ClassNotFoundException {  
        if (rawData == null) {
            return null;
        }
        
        ByteArrayInputStream data = null;
        InflaterInputStream inflaterIS = null;
        ObjectInputStream ois = null;
        try {
            data = new ByteArrayInputStream(rawData);  
            inflaterIS = new InflaterInputStream(data);             
            ois = new ObjectInputStream(inflaterIS);  
            return ois.readObject(); 
        } finally {
            close(ois);
            close(inflaterIS);
            close(data);
        }
    }
       
    public static byte[] deflateObject(Object object) throws IOException { 
        if (object == null) {
            return null;
        }
        
        ByteArrayOutputStream baos = null;
        DeflaterOutputStream deflaterOS = null;
        ObjectOutputStream oos = null;
        try {
            baos = new ByteArrayOutputStream();  
            //TODO more fine-grained control
            //Deflater def = new Deflater(Deflater.BEST_COMPRESSION);  
            deflaterOS = new DeflaterOutputStream(baos, new Deflater());
            oos = new ObjectOutputStream(deflaterOS);
            oos.writeObject(object); 
            deflaterOS.finish();
            oos.flush(); 
            return baos.toByteArray();              
        } finally {
            close(oos);
            close(deflaterOS);
            close(baos);
        }
    }
    
    private static void close(InputStream input) {
        try {
            if (input != null) {
                input.close();
            }
        } catch (Exception e) {             
        }
    }
    
    private static void close(OutputStream output) {
        try {
            if (output != null) {
                output.close();
            }
        } catch (Exception e) { 
        }
    }    

    // GZIP Compression Start
    public static byte[] compressGZip(byte[] content){
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try{
            GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
            gzipOutputStream.write(content);
            gzipOutputStream.close();
            //logger.debug(" Compression Ratio: "+ (1.0f * content.length/byteArrayOutputStream.size()));
        } catch(IOException e){
            throw new RuntimeException(e);
        }
        return byteArrayOutputStream.toByteArray();
    }
    
    // GZIP Compression Ends
}
